// *************************************
// * Creator:SunnyPaine
// * DateTime:2024/08/08 15:08:15
// * Description:<Description>
// * CopyRight:
// * ***********************************

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows.Input;
using Autofac;
using SunnyPaine.Autofac.Extension.Mvvm.Annotations;
using SunnyPaine.Autofac.Extension.Mvvm.Commands;
using SunnyPaine.Autofac.Extension.Mvvm.Exceptions;
using SunnyPaine.Autofac.Extension.Mvvm.UI;
using SunnyPaine.Autofac.Extension.Mvvm.ViewCore;
using SunnyPaine.Autofac.Extension.Stereotype;

namespace SunnyPaine.Autofac.Extension.Mvvm
{
    /// <summary>
    /// 提供基础视图模型信息和处理。
    /// </summary>
    public abstract class BasicViewModel : NotifyPropertyChanged, ICustomTypeDescriptor, ISupportUIServices
    {
        /// <summary>
        /// 命令方法辅助类。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        internal static class CommandHelper<T>
        {
            internal static ICommand CreateCommand(object owner, MethodInfo method, MethodInfo canExecuteMethod, bool hasParameter)
            {
                return new RelayCommand<T>(
                    (p) => { method.Invoke(owner, hasParameter ? new object[1] { p } : Array.Empty<object>()); },
                    (p) =>
                    {
                        return (canExecuteMethod != null) == false
                               || (bool)canExecuteMethod.Invoke(owner, hasParameter ? new object[1] { p } : Array.Empty<object>());
                    });
            }
        }


        #region 私有变量
        private readonly Dictionary<string, CommandDescriptor> commandInfoDict;
        private readonly Dictionary<MethodInfo, ICommand> commands;
        private PropertyDescriptorCollection properties;
        private UIServiceContainer container;
        #endregion


        #region 私有属性，ISupportUIServices成员
        UIServiceContainer ISupportUIServices.Container
        {
            get { return this.container ?? (this.container = new UIServiceContainer()); }
        }
        #endregion


        #region 公共属性
        /// <summary>
        /// 
        /// </summary>
        public UIServiceContainer Container
        {
            get { return this.container; }
        }
        #endregion


        #region 构造方法
        /// <summary>
        /// 使用默认参数创建 <see cref="BasicViewModel"/> 的实例。
        /// </summary>
        public BasicViewModel()
        {
            this.commandInfoDict = new Dictionary<string, CommandDescriptor>();
            this.commands = new Dictionary<MethodInfo, ICommand>();
            this.ParseCommandDescriptor();
        }
        #endregion


        #region 私有方法
        /// <summary>
        /// 解析命令描述。
        /// </summary>
        /// <exception cref="CommandException"></exception>
        private void ParseCommandDescriptor()
        {
            Type type = this.GetType();
            MethodInfo[] mis = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (MethodInfo mi in mis)
            {
                Attribute[] allAttributes = mi.GetCustomAttributes().ToArray();
                CommandAttribute attribute = mi.GetCustomAttribute<CommandAttribute>();
                if (attribute == null)
                {
                    continue;
                }

                string name = attribute.Name ?? (mi.Name.EndsWith("Command") ? mi.Name : mi.Name + "Command");
                if (this.commandInfoDict.ContainsKey(name))
                {
                    throw new CommandException($"已经存在名为 {name} 的命令方法了。");
                }

                // MethodInfo _method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                // if (_method == null)
                // {
                //     throw new CommandException($"找不到命令为 {name} 的方法。");
                // }
                ParameterInfo[] pis4Method = mi.GetParameters();
                if (pis4Method.Length > 1)
                {
                    throw new CommandException($"命令方法 {name} 的参数只能为0个或1个。");
                }

                bool hasCanExecuteMethod = !string.IsNullOrEmpty(attribute.CanExecuteMethodName); //true：存在，false：不存在
                string canExecuteMethodName = hasCanExecuteMethod
                    ? attribute.CanExecuteMethodName
                    : $"Can{mi.Name}";
                MethodInfo _canExecuteMethod = type.GetMethod(canExecuteMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                if (hasCanExecuteMethod && _canExecuteMethod == null)
                {
                    throw new CommandException($"找不到命令为 {canExecuteMethodName} 的方法。");
                }
                if (_canExecuteMethod != null)
                {
                    ParameterInfo[] pis4CanExecuteMethod = _canExecuteMethod.GetParameters();

                    //校验合法性
                    if (pis4Method.Length != pis4CanExecuteMethod.Length)
                    {
                        throw new CommandException($"可执行命令的方法的参数数量错误：{canExecuteMethodName}");
                    }
                    if (pis4Method.Length == 1 && (pis4Method[0].ParameterType != pis4CanExecuteMethod[0].ParameterType || pis4Method[0].IsOut != pis4CanExecuteMethod[0].IsOut))
                    {
                        throw new CommandException($"可执行命令的方法的参数错误：{canExecuteMethodName}");
                    }
                }

                this.commandInfoDict.Add(name, new CommandDescriptor(name, mi, _canExecuteMethod, allAttributes));
            }
        }
        #endregion


        #region 公共方法（内部）
        /// <summary>
        /// 创建ICommand的实例（使用RelayCommand实现）。
        /// </summary>
        /// <param name="method">命令方法。</param>
        /// <param name="canExecuteMethod">是否可执行命令方法。</param>
        /// <param name="hasParameter">命令方法是否有参数。</param>
        /// <returns></returns>
        internal ICommand GetCommand(MethodInfo method, MethodInfo canExecuteMethod, bool hasParameter)
        {
            if (!this.commands.ContainsKey(method))
            {
                // bool flag = method.ReturnType == typeof(Task);
                object[] array = new object[4]
                {
                    this, method, canExecuteMethod, hasParameter
                };
                Type parameterType = hasParameter ? method.GetParameters()[0].ParameterType : typeof(object);
                ICommand command = (ICommand)typeof(CommandHelper<>).MakeGenericType(parameterType)
                    .GetMethod("CreateCommand", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    ?.Invoke(null, array);
                this.commands.Add(method, command);
            }
            return this.commands[method];
        }
        #endregion


        #region 公共方法（受保护）
        /// <summary>
        /// 获取视图的实例（单例）。
        /// </summary>
        /// <param name="viewName">视图名称。</param>
        /// <param name="constructorPatames">
        /// 构造方法的参数。
        /// <para>注意：由于该方法获取视图是以单例模式执行，所以构造方法参数只有在第一次获取实例时会生效执行，从第二次获取实例开始，构造方法参数及时发生了变化也不再生效。</para>
        /// </param>
        /// <returns></returns>
        protected IWindow ResolveView(string viewName, params Tuple<string, object>[] constructorPatames)
        {
            if (constructorPatames != null && constructorPatames.Length > 0)
            {
                return ServiceLocator.Container.Resolve<IViewContext>().Resolve(viewName, constructorPatames);
            }
            return ServiceLocator.Container.Resolve<IViewContext>().Resolve(viewName);
        }

        /// <summary>
        /// 获取视图的新实例。
        /// </summary>
        /// <param name="viewName">视图名称。</param>
        /// <param name="constructorPatames">构造方法参数。</param>
        /// <returns></returns>
        protected IWindow ResolveNewView(string viewName, params Tuple<string, object>[] constructorPatames)
        {
            if (constructorPatames != null && constructorPatames.Length > 0)
            {
                return ServiceLocator.Container.Resolve<IViewContext>().ResolveNew(viewName, constructorPatames);
            }
            return ServiceLocator.Container.Resolve<IViewContext>().ResolveNew(viewName);
        }

        /// <summary>
        /// 获取组件的新实例。
        /// </summary>
        /// <param name="componentName">组件名称。</param>
        /// <param name="constructorPatames">构造方法参数。</param>
        /// <returns></returns>
        protected IControl ResolveComponent(string componentName, params Tuple<string, object>[] constructorPatames)
        {
            if (constructorPatames != null && constructorPatames.Length > 0)
            {
                return ServiceLocator.Container.Resolve<IViewContext>().ResolveControl(componentName, constructorPatames);
            }
            return ServiceLocator.Container.Resolve<IViewContext>().ResolveControl(componentName);
        }
        #endregion


        #region 私有方法，ICustomTypeDescriptor成员（显示实现）
        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, noCustomTypeDesc: true);
        }

        string ICustomTypeDescriptor.GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        string ICustomTypeDescriptor.GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }

        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }

        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, true);
        }

        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            if (this.properties == null)
            {
                IEnumerable<PropertyDescriptor> pds = TypeDescriptor.GetProperties(this, true)
                    .Cast<PropertyDescriptor>()
                    .Concat(this.commandInfoDict.Values);
                this.properties = new PropertyDescriptorCollection(pds.ToArray());
            }
            return this.properties;
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            return TypeDescriptor.GetProperties(this, attributes, true);
        }

        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }
        #endregion
    }
}